﻿//#define Log
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System.IO;
namespace AssetBundleHelper{
	public class ABObject{
		public UnityEngine.Object asset;
		public AssetBundle assetBundle;
		public GameObject gameObject;
		public JSON.JSONNode jsonNode;
		GameObject _prefab;
		public string name;
		private GameObject prefab{
			get{
				if (_prefab == null) {
					if (asset == null) {
						Debug.Log ("name:" + name + " assets is null");
						_prefab = new GameObject (name);
					}else{
						_prefab = GameObject.Instantiate (asset as GameObject);
					}
				}
				return _prefab;
			}
		}
		public void UnLoad(){
			if (assetBundle != null) {
				assetBundle.Unload (true);
				assetBundle = null;
			}
		}

		public void Clear(){
			UnLoad ();
		}
		#region Create
		public ABObject(){
		}
		public ABObject(AssetBundle target,string _name){
			assetBundle = target;
			name = _name;
			asset = target.LoadAsset<UnityEngine.Object> (name.ToLower());
			#if Log
			if(asset == null){
				Debug.Log(string.Format("Load Asset:[{0}] Error AllEssets------",name));
				var ass = target.GetAllAssetNames();
				for(var i = 0;i<ass.Length;i++){
					Debug.Log(ass[i]);
				}
			}
			
			#endif
		}
		public static ABObject CreateJSON(string _name){
			var ab = new ABObject ();
			ab.name = _name;
			var path = string.Format ("Data/{0}", ab.name);
			#if Resources && UNITY_EDITOR
			ab.asset = Resources.Load (path) as TextAsset;
			#endif
			return ab;
		}
		public static ABObject CreateGameObject(string _name,string _path){
			var ab = new ABObject ();
			ab.name = _name;
			var path = string.Format ("{0}{1}", _path,ab.name);
			#if Resources && UNITY_EDITOR
			var _prefab = Resources.Load (path) as GameObject;
			try{
				ab._prefab = GameObject.Instantiate (_prefab);
			}catch{
				Debug.Log ("Load :" + path + " is null");
				ab._prefab = new GameObject (_name);
			}
			#endif
			return ab;
		}
		public static ABObject CreateObject(string _name,string path){
			var ab = new ABObject ();
			var p = string.Format ("{0}{1}", path, _name);
			#if Resources && UNITY_EDITOR
			var obj = Resources.Load (p);
			if (obj == null)
				Debug.Log (string.Format ("Resources：{0} 找不到对象", p)); 
			ab.asset = obj;
			ab.name = _name;
			#endif
			return ab;

		}
		#endregion
		#region GameObject

		public GameObject AddChild(Transform parent,Transform poolparent = null,float deletime = 0){
			if (poolparent == null)
				poolparent = ObjManager.Instance.ParentDefualt;
			gameObject = ObjManager.Instance.AddChildByPool (prefab, parent,poolparent,deletime);
			#if Log
			Debug.Log("gameObject"+ gameObject.name);
			#endif
			gameObject.SetActive (true);
			return gameObject;
		}
		public void Despawn(){
			if(gameObject != null)
				ObjManager.Instance.Despawn (gameObject);
			gameObject = null;
		}
		#endregion
		#region JSON
		public void AsJSONNode(){
			if (asset == null) {
				Debug.Log(string.Format("asset:{0} is null",name));
			}
			if (asset is TextAsset)
				jsonNode = JSON.JSONNode.Parse (((TextAsset)asset).text);
			else
				Debug.Log(string.Format("AssetBundle:{0} is {1}, not textasset",name,asset.GetType().Name));
		}
		#endregion
		#region Scene
		public void LoadScene(){
			Debug.Log ("name:" + name);
			UnityEngine.SceneManagement.SceneManager.LoadSceneAsync (name);
		}
		#endregion
	}
	public class YieldLoadDependencies{
		public int count;
		public System.Action callback;
		public int finish;
		public YieldLoadDependencies(int _count,System.Action _callback){
			finish = 0;
			count = _count;
			callback = _callback;
		}
		public void SomeoneFinish(){
			finish++;
			if (finish >= count) {
				callback ();
			}
		}
	}
	public class AssetBundleData{
		public Dictionary<string,ABObject> dontDestoroy = new Dictionary<string, ABObject> ();	
		public Dictionary<string,ABObject> assets = new Dictionary<string, ABObject> ();
		public Dictionary<string,YieldLoadDependencies> yieldLoadDependencies = new Dictionary<string, YieldLoadDependencies> ();
		public Dictionary<string,List<System.Action<ABObject>>> alreadys = new Dictionary<string, List<System.Action<ABObject>>> ();
		public ABObject assetbundle;
		public AssetBundleManifest assetbundleManifest;
		public List<string> assetsNames;
		public void getAssetBundleMainfest(System.Action callback){
			if (assetbundleManifest == null) {
				AssetAsyncLoader.Instance.Load ("Assetbundle", delegate(string _dbname, AssetBundle obj) {
					assetbundle = new ABObject (obj, "AssetBundleManifest");
					assetbundleManifest = (AssetBundleManifest)assetbundle.assetBundle.LoadAsset ("AssetBundleManifest");
					assetsNames = new List<string>(assetbundleManifest.GetAllAssetBundles());
					#if Log
					Debug.Log("当前资源总数:"+assetsNames.Count);
					for(var i =0;i < assetsNames.Count;i++){
						Debug.Log(assetsNames[i]);
					}
					#endif
	//				Debug.Log ("load AssetBundleMainfest finish  assetbundleManifest != null " + (assetbundleManifest != null));
					callback ();
				});
			} else {
				callback ();
			}
		}
		public ABObject getSceneAB(string _name){
			var abs = AssetAsyncLoader.Instance.Load (_name);
			ABObject ab = new ABObject (abs, _name);
			return ab;
		}
		public ABObject getABObject(string _name,string path){
			#if Resources && UNITY_EDITOR
				return ResourcesLoad(_name,path);
			#else
			if (_name == null || _name == "") {
				Debug.Log ("Error ABObject Name:" + _name);
				return null;
			} else {
				#if Log
				Debug.Log("GetABObject:"+_name);
				#endif
			}
			if (assets.ContainsKey (_name)) {
				return assets [_name];
			} else {
				if (!assetsNames.Contains (_name.ToLower ())) {
					return null;
				} else {
					var dp = assetbundleManifest.GetAllDependencies (_name);
					for (var i = 0; i < dp.Length; i++) {
						var dpab = AssetAsyncLoader.Instance.Load (dp [i]);

						AddAB(dp [i],dpab);
					}
					var ab = AssetAsyncLoader.Instance.Load (_name);
					if (ab == null){
						#if Log
						Debug.Log(string.Format(" Load AB:[{0}] Is Null",_name));
						#endif
						return null;
					}
					else{
						#if Log
						Debug.Log(string.Format(" Load AB:[{0}] {1} Finish",_name,ab.GetType()));
						#endif
						return AddAB (_name, ab);
					}
				}
			}
			#endif
		}

		public void AddABObject(string name,ABObject ab){
			if (name == null || name == "")
				return;
			if(!assets.ContainsKey(name))
				assets.Add (name, ab);
		}
		ABObject AddDontDestroyAB(string name,AssetBundle ab){
			var abobj = new ABObject (ab,name);
			if (!dontDestoroy.ContainsKey (name)) {
	//			Debug.Log ("新增一个 不卸载 资源:" + name);
				dontDestoroy.Add (name, abobj);
			}
			return abobj;
		}
		ABObject AddAB(string name,AssetBundle ab){
			var abobj = new ABObject (ab,name);
			if(!assets.ContainsKey(name))
				assets.Add (name, abobj);
			return abobj;
		}
		ABObject ResourcesLoad(string name,string path){
			var obj = Resources.Load(path+name);
			if(obj == null){
				Debug.Log("Resources Load:"+path+name +" is Null");
			}
			var ab = new ABObject();
			ab.asset = obj;
			ab.name = name;
			AddABObject(name,ab);
			return ab;
		}

		public void getABObject(string name,System.Action<ABObject> callback,string assetsName = "",string pathName = ""){
			#if Resources && UNITY_EDITOR

			callback(ResourcesLoad(name,pathName));
			#else

			getAssetBundleMainfest(delegate() {
				if (assets.ContainsKey (name)) {
					callback (assets [name]);
				} else {
					if(!assetsNames.Contains(name.ToLower ()))
					{
						#if Log
						Debug.Log(string.Format("找不到名为{0}的资源",name.ToLower ()));
						#endif	
						callback(null);
						return;
					}
						
					var dp = assetbundleManifest.GetAllDependencies (name);
					if(dp.Length == 0){
						if(alreadys.ContainsKey(name)){
							alreadys[name].Add(callback);
						}else{
							alreadys.Add(name,new List<System.Action<ABObject>>());
							AssetAsyncLoader.Instance.Load (name, delegate(string backname, AssetBundle obj) {
								var ab = AddAB(name,obj);
								#if Log
								Debug.Log(string.Format("AssetBundle:: Load {0} Finish",name));
								#endif
								callback(ab);
								if(alreadys.ContainsKey(name)){
									for(var i = 0;i < alreadys[name].Count;i++){
										alreadys[name][i](ab);
									}
									alreadys.Remove(name);
								}
							});
						}
					}else{
						if(alreadys.ContainsKey(name)){
							
							alreadys[name].Add(callback);
						}else{
							yieldLoadDependencies.Add(name,new YieldLoadDependencies(dp.Length,delegate() {
								AssetAsyncLoader.Instance.Load (name, delegate(string backname,AssetBundle obj) {
									var ab = AddAB(name,obj);
									#if Log

									Debug.Log(string.Format("AssetBundle:: Load {0} Finish",name));
									var abs = ab.assetBundle.LoadAllAssets();
									for(var i = 0;i <abs.Length;i++){
										Debug.Log(abs[i].name+":"+abs[i].GetType().Name);
									}
									#endif
									callback(ab);
									if(alreadys.ContainsKey(name)){
										for(var i = 0;i < alreadys[name].Count;i++){
											alreadys[name][i](ab);
										}
										alreadys.Remove(name);
									}
								});
								yieldLoadDependencies.Remove(name);
							}));
							for (var i = 0; i < dp.Length; i++) {
								if(alreadys.ContainsKey(dp[i])){
									alreadys[dp[i]].Add(delegate(ABObject obj) {
										yieldLoadDependencies[name].SomeoneFinish();
									});
								}else{
									if(dontDestoroy.ContainsKey(dp[i])){
										yieldLoadDependencies[name].SomeoneFinish();
									}else{
										alreadys.Add(dp[i],new List<System.Action<ABObject>>());
										AssetAsyncLoader.Instance.Load (dp[i], delegate(string _dbname,AssetBundle obj) {
											var ab = AddDontDestroyAB(_dbname,obj);
											yieldLoadDependencies[name].SomeoneFinish();
											if(alreadys.ContainsKey(_dbname)){
												for(var x = 0;x < alreadys[_dbname].Count;x++){
													alreadys[_dbname][x](ab);
												}
												alreadys.Remove(_dbname);
											}
										});
									}
								}
							}
						}
					}
				}
			});
			#endif
		}

		public void OnClear(){
			foreach (var data in assets) {
				data.Value.Clear ();
			}
			assets.Clear ();
		}

	}

	public class AssetBundleDataMgr:SingleClass<AssetBundleDataMgr>{
		public AssetBundleData data = new AssetBundleData ();	
	}

	public struct AssetAsyncLoaderData{
		public System.Action<string,AssetBundle> callback;
		public AssetBundleCreateRequest request;
		public string name;
		public AssetAsyncLoaderData(string _name,System.Action<string,AssetBundle> _callback,AssetBundleCreateRequest _request){
			callback = _callback;
			request = _request;
			name = _name;
		}
	}
	public class AssetAsyncLoader:MonoBehaviour{
		private static AssetAsyncLoader instance;
		public Dictionary<string,AssetAsyncLoaderData> tasks = new Dictionary<string, AssetAsyncLoaderData>();
		string abpath = "";
		public static AssetAsyncLoader Instance{
			get{
				if (instance == null) {
					instance = new GameObject ("AssetAsyncLoader").AddComponent<AssetAsyncLoader> ();
					GameObject.DontDestroyOnLoad (instance.gameObject);
					instance.abpath = Application.persistentDataPath + "/Assetbundle/";
				}
				return instance;
			}
		}
		public AssetBundle Load(string name){
			var p = string.Format("{0}{1}",abpath,name);
			var ab = AssetBundle.LoadFromFile (p);
	#if Log
			Debug.Log(string.Format("Load AB:[{0}] Has Obj {1}",p,ab != null));
	#endif
			return ab;
		}
		public void Load(string name,System.Action<string,AssetBundle> callback){
			if(name != "Assetbundle")
				name = name.ToLower ();
			if (tasks.ContainsKey (name)) {
				#if Log
				Debug.Log ("存在相同的加载任务:" + name);
				#endif
			} else {
				var request = AssetBundle.LoadFromFileAsync (string.Format("{0}{1}",abpath,name));
				#if Log
				Debug.Log ("添加任务:" + name);
				#endif
				tasks.Add (name, new AssetAsyncLoaderData (name, callback, request));
			}
		}
		List<string> deledata = new List<string>();
		public void Update(){
			if (tasks.Count > 0) {
				deledata.Clear ();
				var e = tasks.GetEnumerator ();
				while(e.MoveNext()){
					if (e.Current.Value.request.isDone) {
						deledata.Add (e.Current.Key);
					}
				}
				if (deledata != null) {
					for (var i = 0; i < deledata.Count; i++) {
						var data = tasks [deledata [i]];
						data.callback (data.name,data.request.assetBundle);
						tasks.Remove (deledata [i]);
					}
				}
			}
		}	
	}
}
